//
//  Attachment.m
//  YuchBox
//
//  Created by Ivan on 11-10-17.
//  Copyright 2011年 __MyCompanyName__. All rights reserved.
//

#import "Attachment.h"
#import "StreamPeer.h"
#import "Utility.h"

@implementation Attachment

@synthesize uid             = _uid;
@synthesize uEmailId        = _uEmailId;

@synthesize hashCode        = _hashCode;
@synthesize simpleHashCode  = _simpleHashCode;

@synthesize mailIndex       = _mailIndex;
@synthesize attachmentIndex = _attachmentIndex;
@synthesize attachmentSize  = _attachmentSize;
@synthesize timestamp       = _timestamp;
//@synthesize startIndex      = _startIndex;
@synthesize type            = _type;
@synthesize realName        = _realName;
@synthesize attachmentFileName = _attachmentFileName;
//@synthesize completePercent = _completePercent;

@synthesize status          = _status;
@synthesize isLoad          = _isLoad;

@synthesize cbIndex         = _cbIndex;

+ (NSString *)getAttachTmpDictPath
{
    NSArray *docmentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docmentPath = [docmentPaths objectAtIndex:0];
    NSString *tmpDictPath = [NSString stringWithFormat:@"%@/AttachmentTemp",docmentPath];
    return tmpDictPath;
}

+ (void)creatAttachEnvironment
{
    // Create attachment tmp folder
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSString *tmpDictPath = [self getAttachTmpDictPath];

    if (![fileMgr fileExistsAtPath:tmpDictPath]) {
        // create new folder
        NSError *error = [[NSError alloc] init];
        [fileMgr createDirectoryAtPath:tmpDictPath withIntermediateDirectories:YES attributes:nil error:&error];
        if (0 != [error code]) {
            TTLOGF_INFO(@"Create dictionary <%@> error, code:%d",tmpDictPath, [error code]);
        }
        [error release];
    }
        
    // Create attachment tmp folder
    
    // TODO Create sql structrue
}

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        _status             = ATTACHMENT_STATUS_NOCOPY;
        _isLoad             = NO;
        
        _cbIndex            = 0;
        _attachmentFileName = [[NSString alloc] init];
        //_realName           = [[NSString alloc] init];
        //_type               = [[NSString alloc] init];
    }
    
    return self;
}

- (void)dealloc {
    
    [_attachmentFileName release];
    [_fileOutputStream release];
    [super dealloc];
}

- (id)initWithInputStream:(NSInputStream *)inStream
{
    self = [self init];
    if (self) {
        
        _attachmentSize = [StreamPeer readInt32WithInStream:inStream];
        _realName       = [StreamPeer readStringWithInStream:inStream];
        _type           = [StreamPeer readStringWithInStream:inStream];
        
        //_hashCode       = [[NSString stringWithFormat:@"%@%d",_realName,_attachmentSize] hash];
    }
    
    return self;
}

- (id)readAttachInStream:(NSInputStream *)inStream
{
 
    NSInteger startIndex = [StreamPeer readInt32WithInStream:inStream];
    NSInteger remainSize = [StreamPeer readInt32WithInStream:inStream];
    TTLOGF_INFO(@"Attachment segment <mailindex:%d + attachindex:%d = hashcode: %u>, startIndex:%d, size:%d", _mailIndex, _attachmentIndex
                , [self getSimpleHashCode], startIndex, remainSize);
    
    if (_cbIndex != startIndex) {
        
        TTLOGF_ERROR(@"Some attachment packet may lose! should recieve <index:%d>, acturlly recieve <index:%d>", _cbIndex, startIndex);
    }
//    assert(_cbIndex == startIndex);
    
    [NSOutputStream outputStreamToMemory];
    if (_cbIndex == 0) {
        NSString *tmpFilePath = [self getHexFilePath];
        _fileOutputStream = [[NSOutputStream alloc] initToFileAtPath:tmpFilePath append:NO];
       // _fileOutputStream = [NSOutputStream outputStreamToFileAtPath:tmpFilePath append:NO];
        assert(_fileOutputStream != nil);
        [_fileOutputStream open];
        TTLOGF_INFO(@"Start fetch: <mailindex:%d + attachindex:%d = hashcode: %u>", _mailIndex, _attachmentIndex, [self getSimpleHashCode]);
        
    } 
    UInt8 *buffer = (UInt8 *)malloc(remainSize);
    bzero(buffer, remainSize);
    NSInteger readLen = [StreamPeer readBytesWithInStream:inStream ptr:buffer maxLength:remainSize];
    
    assert(readLen == remainSize);
    
    // write to the file
    UInt8 *bufseeker = buffer;
    NSInteger writeLen = 0;
    assert(_fileOutputStream != nil);  
    while ([_fileOutputStream hasSpaceAvailable]) {
        if (writeLen < remainSize) {
            NSInteger realyWriteLen = [_fileOutputStream write:bufseeker maxLength:(remainSize - writeLen)];            
            if (realyWriteLen == 0 || realyWriteLen == -1) {
                TTLOG_ERROR(@"Write error!");
                continue;
            }
            
            writeLen += realyWriteLen;
            bufseeker += realyWriteLen;
        } else {
            break;
        }
    }

    assert(writeLen == remainSize);
    
    _cbIndex += remainSize;
    free(buffer);
    buffer = nil;
    if (_cbIndex >= _attachmentSize) {
        // read over
        _status = ATTACHMENT_STATUS_DUMPED;
        _isLoad = YES;
        
        TTLOG_INFO(@"Fetch down!");
        
        [_fileOutputStream close];
        _fileOutputStream = nil;
        
        _attachmentFileName = [NSString stringWithFormat:@"%X", [self getHashCode]];
        // !WARNING there are 4 bytes(00 00 00 00) in the end of the file
        // fixed by TZZ
        // [StreamPeer readInt32WithInStream:inStream];
    } else {
        _status = ATTACHMENT_STATUS_DOWNLOADING;
        _isLoad = NO;
    }

    // !TODO resume from break point
    return self;
}
//
//- (NSOutputStream *)getFileOutputStream
//{
//    
//}

- (Boolean)hasLocalAttachmentAndLoad
{
    if (_isLoad) {
        return YES;
    }
    // TODO check the tmp file, whether there were file
    NSString *tmpAttachPath = [self getHexFilePath];
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:tmpAttachPath]) {
        
        return YES;
        // load to memery
//        NSInputStream *fileInStream = [NSInputStream inputStreamWithFileAtPath:tmpAttachPath];
//        if (fileInStream == nil) {
//            TTLOG_ERROR(@"open attachment file stream error!");
//            return NO;
//        }
//        assert(_cbIndex == 0 && _contentBuffer == nil);
//        [fileInStream open];
//        _contentBuffer = (UInt8 *)malloc(_attachmentSize);
//        
//        int readLen = [StreamPeer readBytesWithInStream:fileInStream ptr:_contentBuffer maxLength:_attachmentSize];
//        
//        assert(readLen == _attachmentSize);
//        _isLoad = YES;
//        
//        [fileInStream close];
//        fileInStream = nil;
    }
    
    // need fetch from the server
    _status = ATTACHMENT_STATUS_NOCOPY;
    return NO;
}

- (void)save:(IvanSqliteDB *)con
{
    if (![con checkConnection]) {
        [con connect];
    }

    NSString *sql = [NSString stringWithFormat:@"INSERT INTO Attachment VALUES( \
                     null, %llu, %lu, %d, '%@', \
                     '%@', '%@', %llu, %d, %d)",
                     // id, auto increase
                     _uEmailId, [self getHashCode], _attachmentIndex, _type,
                     _realName, _attachmentFileName, _timestamp, _status, _attachmentSize
                     ];

    TTLOG_INFO(UTIL_TRIM_1(sql));
    if (![con insertWithSql:sql]) {
        TTLOGF_ERROR(@"Insert Attachment <%d> error!", _hashCode);
        return;
    }
    
    _uid = [con getLastInsertRowid];
}

+ (void)retrieveAttachments:(NSMutableArray *)attachments emailId:(UInt64)emailId con:(IvanSqliteDB *)con
{
    if (![con checkConnection]) {
        [con connect];
    }
    
    NSString *sql = [NSString stringWithFormat:@"SELECT * FROM Attachment WHERE fk_email_id = %llu", emailId];
    sqlite3_stmt *stmt = [con prepareSelect:sql];
//    NSLog(@"%@",sql);
    while (SQLITE_ROW == sqlite3_step(stmt)) {
        Attachment *attachment = [[Attachment alloc] init];
        attachment.uid      = (UInt64)sqlite3_column_int64(stmt, 0);
        attachment.hashCode = (UInt32)sqlite3_column_int(stmt, 2);
        attachment.attachmentIndex = (UInt32)sqlite3_column_int(stmt, 4);
        
        attachment.type     = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 4)];
        attachment.realName = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 5)];
        attachment.attachmentFileName = [NSString stringWithUTF8String:(char*)sqlite3_column_text(stmt, 6)];
        attachment.timestamp          = (UInt64)sqlite3_column_int64(stmt, 7);
        attachment.status             = (UInt32)sqlite3_column_int(stmt, 8);
        attachment.attachmentSize     = (UInt32)sqlite3_column_int(stmt, 9);
        
        [attachments addObject:attachment];
        [attachment release];
    }
}

- (NSString *)getHexFilePath
{
//    if (NSOrderedSame == [_attachmentFileName compare:@""]) {
//        
//        NSUInteger hashcode = [self getHashCode];
//       _attachmentFileName  = [NSString stringWithFormat:@"%@/%X",[Attachment getAttachTmpDictPath], hashcode];
//    }

    //TTLOG_INFO(_attachmentFileName);
    return [NSString stringWithFormat:@"%@/%X",[Attachment getAttachTmpDictPath], [self getHashCode]];
}

- (NSUInteger)getHashCode
{
    // for identity the attachment file name
    if (0 == _hashCode) {
        _hashCode = [Attachment generateHashCodeWithMailIndex:_mailIndex attachmentIndex:_attachmentIndex fileName:_realName timestamp:_timestamp];
    }
    return _hashCode;
}


- (NSUInteger)getSimpleHashCode
{
    // for identity the receive attachment
    if (0 == _simpleHashCode) {
        _simpleHashCode = [Attachment generateHashCodeWithMailIndex:_mailIndex attachmentIndex:_attachmentIndex];
    }
    return _simpleHashCode;
}

+ (NSUInteger)generateHashCodeWithMailIndex:(NSInteger)mailIndex attachmentIndex:(NSInteger)attachmentIndex
{
    // TODO use the hash algorithm
    // WARNING this hash maybe not unique
    return [[NSString stringWithFormat:@"%d-%d", mailIndex, attachmentIndex] hash];
}

+ (NSUInteger)generateHashCodeWithMailIndex:(NSInteger)mailIndex attachmentIndex:(NSInteger)attachmentIndex fileName:(NSString *)fileName timestamp:(UInt64)timestamp
{
    // TODO use the hash algorithm
    return [[NSString stringWithFormat:@"%d%d%@%llu", mailIndex, attachmentIndex, fileName, timestamp] hash];
}
@end
